﻿using System;
using System.Linq;
using System.Net;
using System.IO;
using System.IO.Compression;
using UMC.Data;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Net.Sockets;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Security.Authentication;

namespace UMC.Net
{

    public static class NetClient
    {

        static Stream Decompress(Stream response, string encoding)
        {
            switch (encoding)
            {
                case "gzip":
                    return new GZipStream(response, CompressionMode.Decompress);
                case "deflate":
                    return new DeflateStream(response, CompressionMode.Decompress);
                case "br":
                    return new BrotliStream(response, System.IO.Compression.CompressionMode.Decompress);
                default:
                    return response;
            }
        }
        public static NetHttpResponse Get(this NetHttpRequest webRequest, NetWriteData writeData)
        {
            NetHttpResponse netHttpResponse = null; ;
            var mre = new System.Threading.ManualResetEvent(false);
            {
                NetHttpResponse.Create(webRequest, HttpMethod.GET, v =>
               {
                   netHttpResponse = v;
                   v.ReadAsData((b, i, c) =>
                   {
                       if (c == 0 && b.Length == 0)
                       {
                           mre?.Set();
                       }
                       else
                       {
                           writeData(b, i, c);
                       }
                   });
               });

                mre.WaitOne(120000);
                mre.Dispose();
                mre = null;
            }
            return netHttpResponse;
        }
        public static String Get(this NetHttpRequest webRequest, out NetHttpResponse xhr)
        {
            NetHttpResponse netHttpResponse = null; ;
            String resultStr = null;// String.Empty;
            var mre = new System.Threading.ManualResetEvent(false);
            {
                NetHttpResponse.Create(webRequest, HttpMethod.GET, v =>
               {
                   netHttpResponse = v;
                   v.ReadAsString(x =>
                   {
                       resultStr = x;
                       mre?.Set();
                   });
               });
                mre.WaitOne(120000);
                mre.Dispose();
                mre = null;
            }
            xhr = netHttpResponse;
            return resultStr;
        }
        public static byte[] GetBytes(this NetHttpRequest webRequest, out NetHttpResponse xhr)
        {
            NetHttpResponse netHttpResponse = null;
            byte[] resultStr = Array.Empty<byte>();// String.Empty;
            var mre = new System.Threading.ManualResetEvent(false);
            {
                NetHttpResponse.Create(webRequest, HttpMethod.GET, v =>
               {
                   netHttpResponse = v;
                   var ms = new System.IO.MemoryStream();
                   v.ReadAsData((b, i, c) =>
                   {
                       if (c == 0 && b.Length == 0)
                       {
                           if (i == -1)
                           {
                               mre?.Set();
                           }
                           else
                           {
                               resultStr = ms.ToArray();
                               mre?.Set();
                           }
                           ms.Close();
                           ms.Dispose();
                       }
                       else
                       {
                           ms.Write(b, i, c);
                       }
                   });
               });
                mre.WaitOne(120000);
                var m = mre;
                mre = null;
                m.Dispose();
            }
            xhr = netHttpResponse;
            return resultStr;
        }
        public static String Put(this NetHttpRequest webRequest, string value, out NetHttpResponse response)
        {
            NetHttpResponse netHttp = null;
            String result = null;//String.Empty;
            var mre = new System.Threading.ManualResetEvent(false);
            {
                NetHttpResponse.Create(webRequest, HttpMethod.PUT, value, r =>
                {
                    netHttp = r;
                    r.ReadAsString(x =>
                    {
                        result = x;
                        mre?.Set();
                    });
                });
                mre.WaitOne(120000);
                var m = mre;
                mre = null;
                m.Dispose();
            }
            response = netHttp;
            return result;
        }

        public static String Put(this NetHttpRequest webRequest, IJSON value, out NetHttpResponse response)
        {
            webRequest.ContentType = UMC.Net.StringValue.ContentTypeJson;
            return Put(webRequest, UMC.Data.JSON.Serialize(value, "ts"), out response);

        }
        public static void Get(this NetHttpRequest webRequest, Action<NetHttpResponse> prepare)
        {
            NetHttpResponse.Create(webRequest, HttpMethod.GET, prepare);
        }
        public static void Delete(this NetHttpRequest webRequest, Action<NetHttpResponse> prepare)
        {
            NetHttpResponse.Create(webRequest, HttpMethod.DELETE, prepare);
        }

        public static void Put(this NetHttpRequest webRequest, System.Collections.IDictionary value, Action<NetHttpResponse> prepare)
        {
            webRequest.ContentType = UMC.Net.StringValue.ContentTypeJson;
            NetHttpResponse.Create(webRequest, HttpMethod.PUT, UMC.Data.JSON.Serialize(value, "ts"), prepare);
        }
        public static void Put(this NetHttpRequest webRequest, Record value, Action<NetHttpResponse> prepare)
        {
            webRequest.ContentType = UMC.Net.StringValue.ContentTypeJson;
            NetHttpResponse.Create(webRequest, HttpMethod.PUT, UMC.Data.JSON.Serialize(value, "ts"), prepare);
        }
        public static void Put(this NetHttpRequest webRequest, UMC.Data.IJSON value, Action<NetHttpResponse> prepare)
        {
            webRequest.ContentType = UMC.Net.StringValue.ContentTypeJson;
            NetHttpResponse.Create(webRequest, HttpMethod.PUT, UMC.Data.JSON.Serialize(value, "ts"), prepare);
        }
        public static void Put(this NetHttpRequest webRequest, string content, Action<NetHttpResponse> prepare)
        {
            NetHttpResponse.Create(webRequest, HttpMethod.PUT, content, prepare);
        }
        public static void Put(this NetHttpRequest webRequest, System.IO.Stream context, Action<NetHttpResponse> prepare)
        {
            NetHttpResponse.Create(webRequest, HttpMethod.PUT, context, prepare);
        }
        public static void Post(this NetHttpRequest webRequest, System.IO.Stream context, Action<NetHttpResponse> prepare)
        {
            NetHttpResponse.Create(webRequest, HttpMethod.POST, context, prepare);
        }
        public static void Post(this NetHttpRequest webRequest, string context, Action<NetHttpResponse> prepare)
        {
            NetHttpResponse.Create(webRequest, HttpMethod.POST, context, prepare);
        }
        public static void Post(this NetHttpRequest webRequest, IJSON value, Action<NetHttpResponse> prepare)
        {
            webRequest.ContentType = UMC.Net.StringValue.ContentTypeJson;
            NetHttpResponse.Create(webRequest, HttpMethod.POST, UMC.Data.JSON.Serialize(value, "ts"), prepare);
        }
        public static void Post(this NetHttpRequest webRequest, Record value, Action<NetHttpResponse> prepare)
        {
            webRequest.ContentType = UMC.Net.StringValue.ContentTypeJson;
            NetHttpResponse.Create(webRequest, HttpMethod.POST, UMC.Data.JSON.Serialize(value, "ts"), prepare);
        }

        public static void Post(this NetHttpRequest webRequest, System.Collections.IDictionary value, Action<NetHttpResponse> prepare)
        {
            webRequest.ContentType = UMC.Net.StringValue.ContentTypeJson; ;
            NetHttpResponse.Create(webRequest, HttpMethod.POST, UMC.Data.JSON.Serialize(value, "ts"), prepare);
        }

        public static String Post(this NetHttpRequest webRequest, IJSON value, out NetHttpResponse response)
        {
            webRequest.ContentType = UMC.Net.StringValue.ContentTypeJson;
            return Post(webRequest, UMC.Data.JSON.Serialize(value, "ts"), out response);
        }

        public static String Post(this NetHttpRequest webRequest, Record value, out NetHttpResponse response)
        {
            webRequest.ContentType = UMC.Net.StringValue.ContentTypeJson;
            return Post(webRequest, UMC.Data.JSON.Serialize(value, "ts"), out response);

        }
        public static String Post(this NetHttpRequest webRequest, string value, out NetHttpResponse response)
        {
            NetHttpResponse netHttp = null;
            String result = null;//String.Empty;
            var mre = new System.Threading.ManualResetEvent(false);
            {
                NetHttpResponse.Create(webRequest, HttpMethod.POST, value, r =>
                {
                    netHttp = r;
                    r.ReadAsString(x =>
                    {
                        result = x;
                        mre?.Set();
                    });
                });
                mre.WaitOne(120000);
                var m = mre;
                mre = null;
                m.Dispose();
            }
            response = netHttp;
            return result;
        }

        public static void Net(this NetHttpRequest webRequest, HttpMethod method, string context, Action<NetHttpResponse> prepare)
        {
            NetHttpResponse.Create(webRequest, method, context, prepare);
        }

        public static void ReadAsString(this NetHttpResponse webResponse, Action<String> action)
        {
            ReadAsString(webResponse, action, e => { });
        }
        public static void ReadAsString(this NetHttpResponse webResponse, Action<String> action, Action<Exception> error)
        {
            var ms = new TempStream();

            webResponse.ReadAsData((b, i, c) =>
            {
                if (c == 0 && b.Length == 0)
                {
                    if (i == -1)
                    {
                        ms.Close();
                        ms.Dispose();
                        try
                        {
                            error(webResponse.Error);
                        }
                        catch (Exception ex2)
                        {
                            Utility.Error("ReadAsString", webResponse.Error.ToString(), ex2.ToString());
                        }
                    }
                    else
                    {
                        ms.Flush();
                        ms.Position = 0;
                        try
                        {
                            var ct = webResponse.ContentType.Span;
                            Encoding encoding = Encoding.UTF8;
                            var tIndex = ct.IndexOf("charset="u8, true);
                            if (tIndex > 0)
                            {
                                var tsp = ct.Slice(tIndex + 8).UTF8().Trim();
                                encoding = Encoding.GetEncoding(tsp);
                            }

                            using (var str = new System.IO.StreamReader(Decompress(ms, webResponse.Headers.Get(UMC.Net.StringValue.ContentEncoding.Span).Span.UTF8()), encoding))
                            {
                                var value = str.ReadToEnd();
                                action(value);
                            }
                        }
                        catch (Exception ex)
                        {
                            try
                            {
                                error(ex);
                            }
                            catch (Exception ex2)
                            {
                                Utility.Error("ReadAsString", ex.ToString(), ex2.ToString());
                            }
                        }
                        finally
                        {
                            ms.Close();
                            ms.Dispose();
                        }
                    }
                }
                else
                {
                    ms.Write(b, i, c);
                }
            });

        }
        public static void ReadAsStringValue(this NetHttpResponse webResponse, Action<UMC.Net.StringValue> action, Action<Exception> error)
        {
            var ms = new MemoryStream();

            webResponse.ReadAsData((b, i, c) =>
            {
                if (c == 0 && b.Length == 0)
                {
                    if (i == -1)
                    {
                        ms.Close();
                        ms.Dispose();
                        try
                        {
                            error(webResponse.Error);
                        }
                        catch (Exception ex2)
                        {
                            Utility.Error("ReadAsString", webResponse.Error.ToString(), ex2.ToString());
                        }
                    }
                    else
                    {
                        ms.Flush();
                        ms.Position = 0;
                        try
                        {
                            var ct = webResponse.ContentType.Span;
                            Encoding encoding = Encoding.UTF8;
                            var tIndex = ct.IndexOf("charset="u8, true);
                            if (tIndex > 0)
                            {
                                var tsp = ct.Slice(tIndex + 8).UTF8().Trim();
                                encoding = Encoding.GetEncoding(tsp);
                            }
                            var ContentEncoding = webResponse.Headers.Get(UMC.Net.StringValue.ContentEncoding.Span);//.Span.UTF8();
                            if (ContentEncoding.IsEmpty)
                            {
                                action(new UMC.Net.StringValue(ms.ToArray()));
                            }
                            else
                            {
                                var m2s = new MemoryStream();
                                Decompress(ms, ContentEncoding.Span.UTF8()).CopyTo(m2s);
                                action(new UMC.Net.StringValue(m2s.ToArray()));
                                m2s.Dispose();
                            }
                        }
                        catch (Exception ex)
                        {
                            try
                            {
                                error(ex);
                            }
                            catch (Exception ex2)
                            {
                                Utility.Error("ReadAsValue", ex.ToString(), ex2.ToString());
                            }
                        }
                        finally
                        {
                            ms.Close();
                            ms.Dispose();
                        }
                    }
                }
                else
                {
                    ms.Write(b, i, c);
                }
            });

        }
        public static void ReadAsStream(this Net.NetContext context, Action<System.IO.Stream> action)
        {
            ReadAsStream(context, action, context.Error);
        }
        public static void ReadAsStream(this Net.NetContext context, Action<System.IO.Stream> action, Action<Exception> error)
        {
            var ms = new TempStream();

            context.ReadAsData((b, i, c) =>
            {
                if (c == 0 && b.Length == 0)
                {
                    if (i == -1)
                    {
                        ms.Close();
                        ms.Dispose();
                        try
                        {
                            error(new WebException("接收Body错误"));
                        }
                        catch (Exception ex2)
                        {
                            Utility.Error("ReadAsString", ex2.ToString());
                        }
                        error(new WebException("接收Body错误"));
                    }
                    else
                    {
                        ms.Flush();
                        ms.Position = 0;
                        try
                        {
                            action(ms);
                        }
                        catch (Exception ex)
                        {
                            try
                            {
                                error(ex);
                            }
                            catch (Exception ex2)
                            {
                                Utility.Error("ReadAsValue", ex.ToString(), ex2.ToString());
                            }
                        }
                        finally
                        {

                            ms.Close();
                            ms.Dispose();
                        }
                    }
                }
                else
                {
                    ms.Write(b, i, c);
                }
            });


        }
        public static void Net(this NetHttpRequest webRequest, HttpMethod method, System.IO.Stream context, long contextLength, Action<NetHttpResponse> prepare)
        {
            NetHttpResponse.Create(webRequest, method, context, contextLength, prepare);
        }
        public static int WriteBytes(this string str, byte[] bytes, int index)
        {
            return System.Text.Encoding.UTF8.GetBytes(str, 0, str.Length, bytes, index);
        }
        public static Stream TempStream()
        {
            return new TempStream();
        }
        public static void Net(this NetHttpRequest webRequest, Net.NetContext context, Action<NetHttpResponse> prepare)
        {
            webRequest.Method = context.Method;
            if (context.ContentLength >= 0)
            {
                webRequest.ContentType = context.ContentType;
                NetHttpResponse.Create(webRequest, context, prepare);
            }
            else
            {
                NetHttpResponse.Create(webRequest, webRequest.Method, prepare);
            }
        }
        public static bool IsEmpty(this UMC.Net.StringValue stringValue)
        {
            return stringValue == null || stringValue.IsEmpty;//== true;
        }
        public static bool IsEmpty(this String stringValue)
        {
            return String.IsNullOrEmpty(stringValue);//?.IsEmpty == true;
        }
        public static void Header(this NetHttpResponse webResponse, Net.NetContext context)
        {
            context.StatusCode = Convert.ToInt32(webResponse.StatusCode);

            var headers = webResponse.Headers.Span;
            foreach (var h in headers)
            {
                switch (h.HeadKey)
                {
                    case MimeHeadKey.Connection:
                    case MimeHeadKey.KeepAlive:
                    case MimeHeadKey.Server:
                    case MimeHeadKey.TransferEncoding:
                    case MimeHeadKey.AltSvc:
                        break;
                    case MimeHeadKey.AccessControlAllowOrigin:

                        var Origin = context.Headers.Get("Origin"u8);
                        if (Origin.IsEmpty == false)
                        {
                            context.AddHeader(new HeadValue(UMC.Net.StringValue.AccessControlAllowOrigin, Origin));
                        }
                        else
                        {
                            h.Value = new UMC.Net.StringValue("*");
                            context.AddHeader(h);
                        }
                        break;

                    case MimeHeadKey.ContentLength:
                        if (webResponse.IsHead)
                        {
                            context.AddHeader(h);
                        }
                        break;
                    case MimeHeadKey.SetCookie:

                        var value = h.Value.Span;
                        int start = 0, len = value.Length;
                        for (var i = 0; i < len; i++)
                        {
                            if (value[i] == 58 && i == len - 1)
                            {
                                if (start > 0)
                                {
                                    int sIndex = value.IndexOf("secure"u8, true, start);
                                    if (sIndex == -1)
                                    {
                                        sIndex = value.IndexOf("domain="u8, true, start);
                                    }
                                    if (sIndex > 0 && sIndex < i)
                                    {
                                        if (i == len - 1)
                                        {
                                            len = start;
                                        }
                                        else
                                        {
                                            value.Slice(i).CopyTo(value.Slice(start));
                                            len -= i - start;
                                        }
                                    }
                                }

                            }

                        }
                        if (len < value.Length)
                        {
                            h.Value = h.Value.Slice(0, len);
                        }
                        context.AddHeader(h);
                        break;
                    default:
                        context.AddHeader(h);
                        break;
                }

            }
        }
        public static void Transfer(this NetHttpResponse webResponse, Net.NetContext context)
        {

            Header(webResponse, context);

            if (webResponse.IsHead == false)
            {
                if (webResponse.ContentLength > 0)
                {
                    context.ContentLength = webResponse.ContentLength;
                }
            }


            context.UseSynchronousIO();


            webResponse.ReadAsData((b, i, c) =>
            {

                if (c == 0 && b.Length == 0)
                {
                    if (i == -1)
                    {
                        context.Error(webResponse.Error);
                    }
                    else
                    {
                        context.OutputFinish();
                    }
                }
                else
                {
                    context.OutputStream.Write(b, i, c);
                }
            });
        }
        private static readonly UMC.Net.StringValue UserAgent = new UMC.Net.StringValue("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36".UTF8());
        public static NetHttpRequest WebRequest(this Uri url, ByteChunk memory)
        {
            var webRequest = new NetHttpRequest(url, memory);
            webRequest.Headers[UMC.Net.StringValue.UserAgent] = UserAgent;
            return webRequest;
        }
        public static NetHttpRequest WebRequest(this Uri url)
        {
            var webRequest = new NetHttpRequest(url, new ByteChunk());
            webRequest.Headers[UMC.Net.StringValue.UserAgent] = UserAgent;
            return webRequest;
        }
        public static NetHttpRequest WebRequest(this Uri url, UMC.Net.StringValue rawUrl, ByteChunk memory)
        {
            var webRequest = new NetHttpRequest(url, rawUrl, memory);
            webRequest.Headers[UMC.Net.StringValue.UserAgent] = UserAgent;
            return webRequest;
        }
        public static bool Contains(this UMC.Net.StringValue zthis, ReadOnlySpan<byte> bytes)
        {
            return zthis.Span.IndexOf(bytes) > -1;
        }
        public static bool Contains(this UMC.Net.StringValue zthis, ReadOnlySpan<byte> bytes, bool IgnoreCase)
        {
            return zthis.Span.IndexOf(bytes, IgnoreCase) > -1;
        }

        public static bool OutputCache(this NetContext context, Stream mime, String cacheKey)
        {
            var match = context.Headers.Get("If-None-Match"u8);

            // var Since = context.Headers.Get("If-Modified-Since"u8);

            var data = System.Buffers.ArrayPool<byte>.Shared.Rent(200);
            try
            {
                var size = mime.Read(data, 0, data.Length);

                var eTag = UMC.Net.StringValue.Empty;
                var end = UMC.Data.Utility.FindIndexIgnoreCase(data, 0, size, Mime.HeaderEnd);
                if (end > 0)
                {

                    var Origin = context.Headers.Get("Origin"u8);
                    if (Origin.IsEmpty == false)
                    {
                        context.AddHeader(new HeadValue(UMC.Net.StringValue.AccessControlAllowOrigin, Origin));
                    }
                    var isText = false;
                    var isOptimal = false;
                    var start = 0;
                    Span<byte> key = Array.Empty<byte>();
                    for (var i = 0; i <= end; i++)
                    {
                        switch (data[i])
                        {
                            case 10:
                                start = i + 1;
                                break;
                            case 13:
                                var value = data.AsSpan(start, i - start);
                                if (key.SequenceEqual("ETag"u8))
                                {
                                    eTag = context.Memory.Rent(value);
                                    if (value.SequenceEqual(match.Span))
                                    {
                                        context.StatusCode = 304;
                                        context.AddHeader(UMC.Net.StringValue.ETag, eTag);
                                        return true;
                                    }
                                    else
                                    {
                                        // context.AddHeader(UMC.Net.StringValue.XCacheKey, eTag);// Utility.TimeSpan(fileInfo.LastWriteTimeUtc).ToString());
                                        context.AddHeader(UMC.Net.StringValue.ETag, eTag);
                                    }
                                }
                                else if (key.SequenceEqual("ContentType"u8))
                                {

                                    context.ContentType = context.Memory.Rent(value);
                                    context.AddHeader(UMC.Net.StringValue.CacheControl, "public");

                                    isText = value.Contains("text"u8) || value.Contains("javascript"u8);
                                    isOptimal = value.Contains("image/Optimal"u8) || mime.Length == end + 4;
                                }
                                else if (key.SequenceEqual("Content-Encoding"u8))
                                {

                                    context.AddHeader(UMC.Net.StringValue.ContentEncoding, context.Memory.Rent(value));
                                }
                                break;
                            case 58:
                                key = data.AsSpan(start, i - start);

                                start = i + 1;
                                break;
                        }
                    }
                    mime.Seek(end + 4, SeekOrigin.Begin);

                    if (isText)
                    {
                        var ac = context.Headers.Get("Accept-Encoding"u8);
                        if (ac.IsEmpty)
                        {
                            context.ContentLength = mime.Length - end - 4;

                            mime.CopyTo(context.OutputStream);
                        }
                        else
                        {
                            var fStream = mime as FileStream;
                            var nfile = cacheKey;
                            if (ac.Contains("br"u8))
                            {
                                context.AddHeader(UMC.Net.StringValue.ContentEncoding, "br");

                                var file = $"{nfile}.{eTag}.br";
                                if (!File.Exists(file))
                                {
                                    using (var ffU = File.Create(file))
                                    {
                                        var tem = new BrotliStream(ffU, CompressionMode.Compress);
                                        mime.CopyTo(tem);
                                        tem.Flush();
                                        tem.Close();
                                    }

                                }
                                using (var fU = File.OpenRead(file))
                                {
                                    context.ContentLength = fU.Length;
                                    fU.CopyTo(context.OutputStream);
                                }
                            }
                            else if (ac.Contains("gzip"u8))
                            {
                                context.AddHeader(UMC.Net.StringValue.ContentEncoding, "gzip");
                                var file = $"{nfile}.{eTag}.gz";
                                if (!File.Exists(file))
                                {
                                    using (var ffU = File.Create(file))
                                    {
                                        var tem = new GZipStream(ffU, CompressionMode.Compress);
                                        mime.CopyTo(tem);
                                        tem.Flush();
                                        tem.Close();
                                    }

                                }
                                using (var fU = File.OpenRead(file))
                                {
                                    context.ContentLength = fU.Length;
                                    fU.CopyTo(context.OutputStream);
                                }

                            }
                            else if (ac.Contains("deflate"u8))
                            {
                                context.AddHeader(UMC.Net.StringValue.ContentEncoding, "deflate");
                                var file = $"{nfile}.{eTag}.df";
                                if (!File.Exists(file))
                                {
                                    using (var ffU = File.Create(file))
                                    {
                                        var tem = new DeflateStream(ffU, CompressionMode.Compress);
                                        mime.CopyTo(tem);
                                        tem.Flush();
                                        tem.Close();
                                    }

                                }
                                using (var fU = File.OpenRead(file))
                                {
                                    context.ContentLength = fU.Length;
                                    fU.CopyTo(context.OutputStream);
                                }

                            }
                            else
                            {
                                context.ContentLength = mime.Length - end - 4;
                                mime.CopyTo(context.OutputStream);
                            }
                        }

                    }
                    else if (isOptimal)
                    {
                        var nfile = cacheKey;// fStream.Name;
                        var v = context.Headers.Get("Accept"u8);
                        if (v.IsEmpty == false)
                        {
                            if (v.Contains("image/avif"u8))
                            {
                                var file = $"{nfile}.avif";
                                if (File.Exists(file))
                                {
                                    context.ContentType = UMC.Net.StringValue.ContentTypeImageAvif;// "image/avif";
                                    using (var fU = File.OpenRead(file))
                                    {
                                        context.ContentLength = fU.Length;
                                        fU.CopyTo(context.OutputStream);
                                    }
                                    return true;
                                }


                            }
                            if (v.Contains("image/webp"u8))
                            {
                                var file = $"{nfile}.webp";
                                if (File.Exists(file))
                                {

                                    context.ContentType = UMC.Net.StringValue.ContentTypeImageWebP;// "image/webp";

                                    using (var fU = File.OpenRead(file))
                                    {
                                        context.ContentLength = fU.Length;
                                        fU.CopyTo(context.OutputStream);
                                    }
                                    return true;
                                }
                            }

                        }
                        var filePng = $"{nfile}.png";
                        if (File.Exists(filePng))
                        {
                            context.ContentType = UMC.Net.StringValue.ContentTypeImagePng;//"image/png";

                            using (var fU = File.OpenRead(filePng))
                            {
                                context.ContentLength = fU.Length;
                                fU.CopyTo(context.OutputStream);
                            }
                            return true;
                        }
                        return false;
                    }
                    else
                    {

                        context.ContentLength = mime.Length - end - 4;
                        mime.CopyTo(context.OutputStream);
                    }
                    return true;
                }

            }
            finally
            {
                System.Buffers.ArrayPool<byte>.Shared.Return(data);
                mime.Close();
                mime.Dispose();
            }

            return false;
        }
        public static bool EqualsWith(this string context, string str, int index, int len)
        {

            if (len == context.Length && len + index <= str.Length)
            {
                var start = context.Length - len;
                for (var i = 0; i < len; i++)
                {
                    if (Char.ToLower(context[start + i]) != Char.ToLower(str[index + i]))
                    {
                        return false;
                    }
                }
                return true;
            }
            return false;
        }
        public static bool EndsWith(this string context, string str, int index, int len)
        {
            if (len <= context.Length && len + index <= str.Length)
            {
                var start = context.Length - len;
                for (var i = 0; i < len; i++)
                {
                    if (Char.ToLower(context[start + i]) != Char.ToLower(str[index + i]))
                    {
                        return false;
                    }
                }
                return true;
            }
            return false;
        }
        public static bool EndsWith(this string context, string str, int index)
        {
            var len = str.Length - index;
            return EndsWith(context, str, index, len);
        }
        public static bool StartsWith(this string context, string str, int index)
        {
            var len = str.Length - index;
            return StartsWith(context, str, index, len);
        }
        public static bool StartsWith(this string context, string str, int index, int len)
        {
            if (len <= context.Length && len + index <= str.Length)
            {
                for (var i = 0; i < len; i++)
                {
                    if (Char.ToLower(context[i]) != Char.ToLower(str[index + i]))
                    {
                        return false;
                    }
                }
                return true;
            }
            return false;
        }

        public static bool CheckCache(this NetContext context, string root, String vs, out String cacheKey)
        {
            var version = vs ?? String.Empty;
            var md5 = System.Security.Cryptography.MD5.Create();// MD5CryptoServiceProvider();
            var RawUrl = context.PathQuery.Span;
            var r = RawUrl.IndexOf("&404="u8);
            if (r > -1)
            {
                RawUrl = RawUrl.Slice(0, r);
            }
            var bytes = System.Buffers.ArrayPool<byte>.Shared.Rent(RawUrl.Length + System.Text.Encoding.UTF8.GetByteCount(version));
            try
            {
                if (RawUrl.EndsWith("&umc=src"u8) || RawUrl.EndsWith("?umc=src"u8))
                {
                    RawUrl.Slice(0, RawUrl.Length - 8).CopyTo(bytes);
                    var i = RawUrl.Length - 8;// System.Text.Encoding.UTF8.GetBytes(RawUrl, 0, RawUrl.Length - 8, bytes, 0);
                    i += System.Text.Encoding.UTF8.GetBytes(version, 0, version.Length, bytes, i);
                    var md = new Guid(md5.ComputeHash(bytes, 0, i));
                    cacheKey = UMC.Data.Reflection.ConfigPath(String.Format("Cache/{0}/{1}.che", root, md));

                    return false;
                }
                else
                {
                    RawUrl.CopyTo(bytes);
                    var i = RawUrl.Length;// System.Text.Encoding.UTF8.GetBytes(RawUrl, 0, RawUrl.Length, bytes, 0);
                    i += System.Text.Encoding.UTF8.GetBytes(version, 0, version.Length, bytes, i);
                    var md = new Guid(md5.ComputeHash(bytes, 0, i));
                    cacheKey = UMC.Data.Reflection.ConfigPath(String.Format("Cache/{0}/{1}.che", root, md));

                    if (System.IO.File.Exists(cacheKey))
                    {
                        using (var stream = File.OpenRead(cacheKey))
                        {
                            return OutputCache(context, stream, cacheKey);
                        }
                    }
                    return false;
                }
            }
            finally
            {
                System.Buffers.ArrayPool<byte>.Shared.Return(bytes);

            }
        }
        public static async Task<Mime> GetAsync(this NetHttpRequest request, CancellationToken cancellationToken = default)
        {
            var memory = request.Memory;
            TcpClient client = new TcpClient();
            using (client)
            {

                await client.ConnectAsync(request.Address.Host, request.Address.Port, cancellationToken);
                Stream stream;
                if (request.Address.Scheme == "https")
                {
                    SslStream ssl = new SslStream(client.GetStream(), false, (a, b, c, d) => true);
                    await ssl.AuthenticateAsClientAsync(request.Address.Host, new X509CertificateCollection(), SslProtocols.None, false);
                    stream = ssl;
                }
                else
                {
                    stream = client.GetStream();
                }
                using (stream)
                {
                    NetHttpResponse.Header(request, (b, c, t) =>
                    {
                        memory.Temp(b.AsSpan(c, t));
                    });
                    await stream.WriteAsync(memory.Temp().Memory, cancellationToken);

                    memory.Reset();

                    var b = Mime.CreateMime(memory);
                    var buffer = System.Buffers.ArrayPool<byte>.Shared.Rent(0x200);
                    try
                    {
                        int l = 0;
                        while (b.IsMimeFinish == false && b.IsHttpFormatError == false && (l = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0)
                        {
                            b.Receive(buffer, 0, l);
                        }
                    }
                    finally
                    {
                        System.Buffers.ArrayPool<byte>.Shared.Return(buffer);
                    }
                    return b;
                }

            }
        }

        public static System.IO.Stream MimeStream(string file, UMC.Net.StringValue contentType, uint tag)
        {
            if (!System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(file)))
            {
                System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(file));
            }

            System.IO.Stream sWriter = File.Open(file, FileMode.Create, FileAccess.ReadWrite);

            var data = System.Buffers.ArrayPool<byte>.Shared.Rent(100);
            try
            {
                if (contentType.IsEmpty)
                {
                    sWriter.Write(data, 0, $"ETag:{tag}\r\n\r\n".WriteBytes(data, 0));

                }
                else
                {
                    sWriter.Write(data, 0, $"ContentType:{contentType}\r\nETag:{tag}\r\n\r\n".WriteBytes(data, 0));
                }
            }
            finally
            {
                System.Buffers.ArrayPool<byte>.Shared.Return(data);
            }


            return sWriter;

        }
        public static UMC.Net.StringValue Get(this ReadOnlySpan<HeadValue> headValues, ReadOnlySpan<byte> key)
        {
            foreach (var k in headValues)
            {
                if (Utility.EqualIgnoreCase(k.Name.Span, key))
                {
                    return k.Value;

                }
            }
            return UMC.Net.StringValue.Empty;
        }
        public static String UTF8(this byte[] bytes)
        {
            if (bytes == null)
            {
                return String.Empty;
            }
            return UTF8Encoding.UTF8.GetString(bytes);
        }
        public static int? Int32(this ReadOnlySpan<byte> bytes)
        {
            if (bytes.Length > 4)
            {
                return BitConverter.ToInt32(bytes);
            }
            return null;// UTF8Encoding.UTF8.GetString(bytes.Span);
        }
        public static int? Int32(this Span<byte> bytes)
        {
            if (bytes.Length > 4)
            {
                return BitConverter.ToInt32(bytes);
            }
            return null;// UTF8Encoding.UTF8.GetString(bytes.Span);
        }
        public static int? Int32(this UMC.Net.StringValue bytes)
        {
            if (bytes == null || bytes.Length < 4)
            {
                return null;
            }
            return BitConverter.ToInt32(bytes.Span);
        }
        public static String UTF8(this UMC.Net.StringValue bytes)
        {
            return bytes == null ? null : UTF8Encoding.UTF8.GetString(bytes.Span);
        }


        public static ulong? UInt64(this UMC.Net.StringValue bytes)
        {
            return bytes == null || bytes.Length < 8 ? null : BitConverter.ToUInt64(bytes.Span);
        }
        public static long? Int64(this UMC.Net.StringValue bytes)
        {
            return bytes == null || bytes.Length < 8 ? null : BitConverter.ToInt64(bytes.Span);
        }
        public static short? Int16(this UMC.Net.StringValue bytes)
        {
            return bytes == null || bytes.Length < 2 ? null : BitConverter.ToInt16(bytes.Span);
        }
        public static UMC.Net.StringValue StringValue(this short bytes)
        {
            return new UMC.Net.StringValue(BitConverter.GetBytes(bytes));
        }
        public static UMC.Net.StringValue StringValue(this ushort bytes)
        {
            return new UMC.Net.StringValue(BitConverter.GetBytes(bytes));
        }
        public static UMC.Net.StringValue StringValue(this int bytes)
        {
            return new UMC.Net.StringValue(BitConverter.GetBytes(bytes));
        }
        public static UMC.Net.StringValue StringValue(this uint bytes)
        {
            return new UMC.Net.StringValue(BitConverter.GetBytes(bytes));
        }

        public static UMC.Net.StringValue StringValue(this long bytes)
        {
            return new UMC.Net.StringValue(BitConverter.GetBytes(bytes));
        }
        public static UMC.Net.StringValue StringValue(this ulong bytes)
        {
            return new UMC.Net.StringValue(BitConverter.GetBytes(bytes));
        }


        public static ushort? UInt16(this UMC.Net.StringValue bytes)
        {
            return bytes == null || bytes.Length < 2 ? null : BitConverter.ToUInt16(bytes.Span);
        }


        public static uint? UInt32(this UMC.Net.StringValue bytes)
        {
            return bytes == null || bytes.Length < 4 ? null : BitConverter.ToUInt32(bytes.Span);
        }

        public static String UTF8(this Span<byte> bytes)
        {
            return UTF8Encoding.UTF8.GetString(bytes);
        }
        public static String UTF8(this ReadOnlySpan<byte> bytes)
        {
            return UTF8Encoding.UTF8.GetString(bytes);
        }
        public static byte[] UTF8(this String bytes)
        {
            return UTF8Encoding.UTF8.GetBytes(bytes);
        }
        public static String UTF8(this Memory<byte> bytes)
        {
            return UTF8Encoding.UTF8.GetString(bytes.Span);
        }
        public static bool Contains(this ReadOnlySpan<byte> bytes, ReadOnlySpan<byte> bytes1)
        {
            return bytes.IndexOf(bytes1) > -1;
        }
        public static bool Contains(this Span<byte> bytes, ReadOnlySpan<byte> bytes1)
        {
            return bytes.IndexOf(bytes1) > -1;
        }
        public static int IgnoreCaseCompareTo(this Span<byte> bytes, ReadOnlySpan<byte> bytes1)
        {
            int m = Math.Min(bytes.Length, bytes1.Length);
            for (int i = 0; i < m; i++)
            {
                var c = IgnoreCaseCompareTo(bytes[i], bytes1[i]);
                if (c != 0)
                {
                    return c;
                }
            }
            return bytes.Length.CompareTo(bytes1.Length);
        }
        public static int IgnoreCaseCompareTo(this ReadOnlySpan<byte> bytes, ReadOnlySpan<byte> bytes1)
        {
            int m = Math.Min(bytes.Length, bytes1.Length);
            for (int i = 0; i < m; i++)
            {
                var c = IgnoreCaseCompareTo(bytes[i], bytes1[i]);
                if (c != 0)
                {
                    return c;
                }
            }
            return bytes.Length.CompareTo(bytes1.Length);
        }

        public static bool Contains(this ReadOnlySpan<byte> bytes, ReadOnlySpan<byte> bytes1, bool IgnoreCase)
        {
            return bytes.IndexOf(bytes1, IgnoreCase) > -1;
        }
        public static int IgnoreCaseCompareTo(byte x, byte y)
        {
            if (x > 96 && x < 123)
            {
                x -= 32;
            }
            if (y > 96 && y < 123)
            {
                y -= 32;
            }

            return x.CompareTo(y);
        }
        public static bool Contains(this Memory<byte> bytes, ReadOnlySpan<byte> bytes1)
        {
            return bytes.Span.IndexOf(bytes1) > -1;
        }

        public static int IndexOf(this Span<byte> bf, ReadOnlySpan<byte> search, bool IgnoreCase, int offset = 0)
        {
            if (IgnoreCase)
            {
                return Utility.FindIgnoreCase(bf, search, offset);
            }
            return IndexOf(bf, search, offset);
        }

        public static int IndexOf(this ReadOnlySpan<byte> bf, ReadOnlySpan<byte> search, bool IgnoreCase, int offset = 0)
        {
            if (IgnoreCase)
            {
                return Utility.FindIgnoreCase(bf, search, offset);
            }
            return IndexOf(bf, search, offset);
        }

        public static int IndexOf<T>(this Span<T> bf, T value, int offset) where T : IEquatable<T>
        {
            var l = bf.Slice(offset).IndexOf(value);
            return l > -1 ? l + offset : l;
        }
        public static int LastIndexOf<T>(this Span<T> bf, T value, int offset) where T : IEquatable<T>
        {
            return bf.Slice(0, offset).LastIndexOf(value);
            // return l > -1 ? l + offset : l;
        }

        public static int IndexOf(this ReadOnlySpan<byte> bf, ReadOnlySpan<byte> search, int offset = 0)
        {
            for (; offset < bf.Length; offset++)
            {
                if (bf[offset] == search[0])
                {
                    if (offset + search.Length - 1 < bf.Length)
                    {
                        bool IsFind = true;
                        for (int c = 1; c < search.Length; c++)
                        {
                            if (search[c] != bf[offset + c])
                            {
                                IsFind = false;
                                break;
                            }
                        }
                        if (IsFind)
                        {
                            return offset;
                        }

                    }
                    else
                    {
                        return -1;
                    }
                }
            }
            return -1;
        }
        static UMC.Net.StringValue AcceptEncoding = new UMC.Net.StringValue("gzip, deflate, br");
        public static NetHttpRequest Transfer(this Net.NetContext context, Uri url)
        {

            var webRequest = new NetHttpRequest(url, context.Memory);
            var Headers = context.Headers.Span;
            foreach (var v in Headers)
            {
                switch (v.HeadKey)
                {
                    case MimeHeadKey.Host:
                    case MimeHeadKey.Connection:
                    case MimeHeadKey.ContentLength:
                    case MimeHeadKey.Cookie:
                        break;
                    case MimeHeadKey.AcceptEncoding:
                        webRequest.Add(new HeadValue(v.Name, AcceptEncoding));//new UMC. Net.StringValue("gzip, deflate, br")));
                        break;
                    default:
                        webRequest.Add(v);
                        break;

                }
            }

            return webRequest;

        }
    }
}


